import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

// UDP 版本的回显服务器
public class UdpEchoServer {
    // 网络编程，本质上是要操作网卡，但网卡不方便直接操作，
    // 故在操作系统内核中，使用一种特殊地叫做socket这样的文件来抽象表示网卡
    // 因此在进行网络通信时，需要先有一个socket对象
    private DatagramSocket socket = null;

    // 对于服务器来说，创建socket对象的同时，要绑定上一个具体端口号
    // 服务器一定要关联一个具体的端口号，因为服务器是网络传输中的被动一方
    // 如果是操作系统随机分配的端口号，此时客户端就不知道具体的端口号，也就无法进行网络通信
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动！");
        // 因服务器不是只给一个客户端提供通信的服务，需要服务多个客户端，故要使用while循环
        while(true){
            // 只要客户端将请求传送过来，服务器就可以提供服务
            // 1,读取客户端发来的请求并解析
            // 切记：receive的参数为输出型参数，这就需要先构造一个空白的DatagramPacket对象，然后再交给receive进行填充
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            // 等待接收客户端发送的UDP数据报，该方法在接收到数据报之前会一直阻塞，接收到数据报以后，DatagramPacket对象包含数据（bytes）,客户端ip和端口号
            socket.receive(requestPacket);
            // 此时这个DatagramPacket对象是一个特殊的对象，并不方便直接进行处理，就可以将其里面的数据拿出来构造成字符串
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());
            // 2,根据请求计算响应，由于此处是回显服务器，故请求和响应一样
            String response = process(request);
            // 3,把响应写回到客户端，send的参数也是DatagramPacket对象，故需要先构造一个DatagramPacket对象
            // 切记：此处构造的响应对象，不能使用空的字节数组构造，而应该使用响应的数据来构造
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            // 4，打印日志信息，当前这次请求响应的中间处理结果
            System.out.printf("[%s:%d] req: %s; resp: %s %n",requestPacket.getAddress().toString(),requestPacket.getPort(),
                    request,response);
        }
    }

    // 根据请求计算响应
    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        // 端口号的设定，1024~65535间随便即可
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}
